#include "cpu/irq.h"
#include "comm/cpu_instr.h"
#include "os_cfg.h"
#include "tools/log.h"
#include "applib/applib.h"

static gate_descriptor idt_table[IDT_TABLE_SIZE];

//设置门描述符
void set_gate_descriptor(gate_descriptor* desc,uint16_t selector,uint32_t offset,uint16_t attr)
{
    desc->selector=selector;
    desc->attr=attr;
    desc->offset15_0=offset & 0xffff;
    desc->offset31_16=(offset >> 16) & 0xffff;
}

static void dump_core_regs (exception_information * frame) 
{
    uint32_t esp, ss;
    if (frame->cs & 0x7) {
        ss = frame->ds;
        esp = frame->esp;
    } else {
        ss = frame->ss3;
        esp = frame->esp3;
    }
    print_log("IRQ: %d, error code: %d.", frame->num, frame->error_code);
    print_log("CS: %x\nDS: %x\nES: %x\nSS: %x\nFS:%x\nGS:%x",
               frame->cs, frame->ds, frame->es, ss, frame->fs, frame->gs
    );
    print_log("EAX:0x%x\n"
                "EBX:0x%x\n"
                "ECX:0x%x\n"
                "EDX:0x%x\n"
                "EDI:0x%x\n"
                "ESI:0x%x\n"
                "EBP:0x%x\n"
                "ESP:0x%x\n",
               frame->eax, frame->ebx, frame->ecx, frame->edx,
               frame->edi, frame->esi, frame->ebp, esp);
    print_log("EIP:0x%x\nEFLAGS:0x%x\n", frame->eip, frame->eflags);
}


static void do_default_handler (exception_information* frame, const char * message) 
{
    print_log("--------------------------------");
    print_log("IRQ/Exception happend: %s.", message);
    dump_core_regs(frame);
    print_log("--------------------------------");

    if (frame->cs & 0x3) 
    {
        sys_exit(frame->error_code);
    } 
    else 
    {
        for (;;) 
        {
            hlt();
        }
    }

}

void do_handler_unknown (exception_information * frame) 
{
	do_default_handler(frame, "Unknown exception.");
}

void do_handler_divzero(exception_information * frame) 
{
	do_default_handler(frame, "Device Error.");
}

void do_handler_Debug(exception_information * frame) 
{
	do_default_handler(frame, "Debug Exception");
}

void do_handler_NMI(exception_information * frame) 
{
	do_default_handler(frame, "NMI Interrupt.");
}

void do_handler_breakpoint(exception_information * frame) 
{
	do_default_handler(frame, "Breakpoint.");
}

void do_handler_overflow(exception_information * frame) 
{
	do_default_handler(frame, "Overflow.");
}

void do_handler_bound_range(exception_information * frame) 
{
	do_default_handler(frame, "BOUND Range Exceeded.");
}

void do_handler_invalid_opcode(exception_information * frame) 
{
	do_default_handler(frame, "Invalid Opcode.");
}

void do_handler_device_unavailable(exception_information * frame) 
{
	do_default_handler(frame, "Device Not Available.");
}

void do_handler_double_fault(exception_information * frame) 
{
	do_default_handler(frame, "Double Fault.");
}

void do_handler_invalid_tss(exception_information * frame) 
{
	do_default_handler(frame, "Invalid TSS");
}

void do_handler_segment_not_present(exception_information * frame) 
{
	do_default_handler(frame, "Segment Not Present.");
}

void do_handler_stack_segment_fault(exception_information * frame) 
{
	do_default_handler(frame, "Stack-Segment Fault.");
}

void do_handler_general_protection(exception_information * frame) 
{
    print_log("--------------------------------");
    print_log("IRQ/Exception happend: General Protection.");
    if (frame->error_code & ERR_EXT) {
        print_log("the exception occurred during delivery of an "
                "event external to the program, such as an interrupt"
                "or an earlier exception.");
    } else {
        print_log("the exception occurred during delivery of a"
                    "software interrupt (INT n, INT3, or INTO).");
    }
    
    if (frame->error_code & ERR_IDT) {
        print_log("the index portion of the error code refers "
                    "to a gate descriptor in the IDT");
    } else {
        print_log("the index refers to a descriptor in the GDT");
    }
    
    ("segment index: %d", frame->error_code & 0xFFF8);

    dump_core_regs(frame);
    if (frame->cs & 0x3) 
    {
        sys_exit(frame->error_code);
    } else 
    {
        for (;;) 
        {
            hlt();
        }
    }
}

void do_handler_page_fault(exception_information * frame) 
{
    print_log("-------------");
    print_log("page falut.");
	if (frame->error_code & ERR_PAGE_P) 
    {
        print_log("\tpage-level protection violation: 0x%x.", read_cr2());
    } else 
    {
        print_log("\tPage doesn't present 0x%x", read_cr2());
    }
    
    if (frame->error_code & ERR_PAGE_WR) 
    {
        print_log("\tThe access causing the fault was a read.");
    } 
    else 
    {
        print_log("\tThe access causing the fault was a write.");
    }
    
    if (frame->error_code & ERR_PAGE_US) 
    {
        print_log("\tA supervisor-mode access caused the fault.");
    } 
    else 
    {
        print_log("\tA user-mode access caused the fault.");
    }

    dump_core_regs(frame);
    
    if (frame->cs & 0x3) 
    {
        sys_exit(frame->error_code);
    } else 
    {
        for (;;) 
        {
            hlt();
        }
    }    
}

void do_handler_fpu_error(exception_information * frame) 
{
	do_default_handler(frame, "X87 FPU Floating Point Error.");
}

void do_handler_alignment_check(exception_information * frame) 
{
	do_default_handler(frame, "Alignment Check.");
}

void do_handler_machine_check(exception_information * frame) 
{
	do_default_handler(frame, "Machine Check.");
}

void do_handler_smd_exception(exception_information * frame) 
{
	do_default_handler(frame, "SIMD Floating Point Exception.");
}

void do_handler_virtual_exception(exception_information * frame) 
{
	do_default_handler(frame, "Virtualization Exception.");
}


int irq_install(int irq_num,uint32_t handler)
{
    if(irq_num >= IDT_TABLE_SIZE)
    {
        return -1;
    }

    set_gate_descriptor(idt_table + irq_num, (KERNEL_CS << 3), (uint32_t) handler,
                  GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_IDT);
    
    return 0;
}



//初始化中断控制器
void init_pic()
{
     // 边缘触发，级联，需要配置icw4, 8086模式
    outb(PIC0_ICW1, PIC_ICW1_ALWAYS_1 | PIC_ICW1_ICW4);

    // 对应的中断号起始序号0x20
    outb(PIC0_ICW2, IRQ_PIC_START);

    // 主片IRQ2有从片
    outb(PIC0_ICW3, 1 << 2);

    // 普通全嵌套、非缓冲、非自动结束、8086模式
    outb(PIC0_ICW4, PIC_ICW4_8086);

    // 边缘触发，级联，需要配置icw4, 8086模式
    outb(PIC1_ICW1, PIC_ICW1_ICW4 | PIC_ICW1_ALWAYS_1);

    // 起始中断序号，要加上8
    outb(PIC1_ICW2, IRQ_PIC_START + 8);

    // 没有从片，连接到主片的IRQ2上
    outb(PIC1_ICW3, 2);

    // 普通全嵌套、非缓冲、非自动结束、8086模式
    outb(PIC1_ICW4, PIC_ICW4_8086);

    // 禁止所有中断, 允许从PIC1传来的中断
    outb(PIC0_IMR, 0xFF & ~(1 << 2));
    outb(PIC1_IMR, 0xFF);
}



void irq_init()
{
    //初始化IDT表项
    for(int i=0;i<IDT_TABLE_SIZE;i++)
    {
        set_gate_descriptor(idt_table+i,KERNEL_CS << 3,(uint32_t)exception_handler_unknown,GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_IDT);
    }

    //设置除零异常
    irq_install(IRQ0_DE, (uint32_t)exception_handler_divzero);
	irq_install(IRQ1_DB, (uint32_t)exception_handler_Debug);
	irq_install(IRQ2_NMI, (uint32_t)exception_handler_NMI);
	irq_install(IRQ3_BP, (uint32_t)exception_handler_breakpoint);
	irq_install(IRQ4_OF, (uint32_t)exception_handler_overflow);
	irq_install(IRQ5_BR, (uint32_t)exception_handler_bound_range);
	irq_install(IRQ6_UD, (uint32_t)exception_handler_invalid_opcode);
	irq_install(IRQ7_NM, (uint32_t)exception_handler_device_unavailable);
	irq_install(IRQ8_DF, (uint32_t)exception_handler_double_fault);
	irq_install(IRQ10_TS, (uint32_t)exception_handler_invalid_tss);
	irq_install(IRQ11_NP, (uint32_t)exception_handler_segment_not_present);
	irq_install(IRQ12_SS, (uint32_t)exception_handler_stack_segment_fault);
	irq_install(IRQ13_GP, (uint32_t)exception_handler_general_protection);
	irq_install(IRQ14_PF, (uint32_t)exception_handler_page_fault);
	irq_install(IRQ16_MF, (uint32_t)exception_handler_fpu_error);
	irq_install(IRQ17_AC, (uint32_t)exception_handler_alignment_check);
	irq_install(IRQ18_MC, (uint32_t)exception_handler_machine_check);
	irq_install(IRQ19_XM, (uint32_t)exception_handler_smd_exception);
	irq_install(IRQ20_VE, (uint32_t)exception_handler_virtual_exception);
    //设置idtr寄存器 
    lidt((uint32_t)idt_table,sizeof(idt_table));

    init_pic();
}

//开启特定中断
void  int_open(int int_num)
{
    if(int_num < IRQ_PIC_START)
    {
        return;
    }

    int_num -= IRQ_PIC_START;
    if(int_num < 8)
    {
        uint8_t mask = inb(PIC0_IMR) & ~ (1<<int_num);
        outb(PIC0_IMR, mask);
    }
    else
    {
        int_num -= 8;
        uint8_t mask= inb(PIC1_IMR) & ~ (1<<int_num);
        outb(PIC1_IMR, mask);
    }
}

//开启特定中断
void  int_close(int int_num)
{
    if(int_num < IRQ_PIC_START)
    {
        return;
    }

    int_num -= IRQ_PIC_START;
    if(int_num < 8)
    {
        uint8_t mask = inb(PIC0_IMR) | (1<<int_num);
        outb(PIC0_IMR, mask);
    }
    else
    {
        int_num -= 8;
        uint8_t mask= inb(PIC1_IMR)  | (1<<int_num);
        outb(PIC1_IMR, mask);
    }
}


//关闭全局中断
void close_global_int()
{
    cli();
}


//打开全局中断
void open_global_int()
{
    sti();
}


void pic_send_eoi(int int_num)
{
    int_num -= IRQ_PIC_START;
    if(int_num >= 8)
    {
        outb(PIC1_OCW2,PIC0_OCW2_EOI);
    }

    outb(PIC0_OCW2,PIC0_OCW2_EOI);
}

irq_state int_enter_protection()
{
    irq_state state=read_eflags();
    close_global_int();
    return state;
}

void int_leave_protection(irq_state state)
{
    write_eflags(state);
}